package com.sali.application.sysuser.service.impl;

import com.google.code.kaptcha.Constants;
import com.sali.application.sysuser.contants.SysUserContants;
import com.sali.application.sysuser.convert.SysUserConvertModelToVO;
import com.sali.application.sysuser.model.request.SysUserRequest;
import com.sali.application.sysuser.model.vo.SysUserDetailVO;
import com.sali.application.sysuser.model.vo.SysUserVO;
import com.sali.application.sysuser.service.ISystemUserService;
import com.sali.rbac.enums.RbacResultEnum;
import com.sali.rbac.model.SysApisModel;
import com.sali.rbac.model.SysMenusModel;
import com.sali.rbac.model.SysRoleModel;
import com.sali.rbac.model.SysUserModel;
import com.sali.rbac.repository.ISysApisRepository;
import com.sali.rbac.repository.ISysMenusRepository;
import com.sali.rbac.repository.ISysRoleRepository;
import com.sali.rbac.repository.ISysUserRepository;
import com.sali.redis.util.RedisUtil;
import com.sali.utils.exception.ApiException;
import com.sali.utils.exception.BusinessException;
import com.sali.utils.methods.JsonUtil;
import com.sali.utils.methods.jwt.JwtUtil;
import com.sali.utils.methods.pwd.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.sali.rbac.enums.RbacResultEnum.ROLE_API_EX;

/**
 * @description:
 * @author: sali
 * @date: 2024/7/20 12:11.
 * @version: 1.0
 */
@Service
@Slf4j
public class SystemUserService implements ISystemUserService {

    @Autowired
    private ISysUserRepository sysUserRepository;

    @Autowired
    private ISysRoleRepository sysRoleRepository;

    @Autowired
    private ISysMenusRepository sysMenusRepository;

    @Autowired
    private ISysApisRepository sysApisRepository;

    @Autowired
    private SysUserConvertModelToVO sysUserConvertModelToVO;

    @Resource
    private RedisUtil<SysUserVO> redisUtil;


    public SysUserVO selectSysUserByAccount(String account) {
        String cacheKey = "usercache:getUserByAccount:" + account;
        SysUserVO sysUserDTO  = redisUtil.get(cacheKey);
        if (sysUserDTO == null ) {
            sysUserDTO = sysUserConvertModelToVO.convert( sysUserRepository.selectBySysUserByAccount(account) );
            redisUtil.setNx(cacheKey, sysUserDTO, 1L, TimeUnit.MINUTES);
        }
        return sysUserDTO;
    }

    @Override
    public SysUserVO adminLogin(SysUserRequest sysUserRequest) {
        // 1：根据account查询用户信息是否存在，如果不存在直接返回用户不正确
        SysUserModel sysUserModel = sysUserRepository.selectBySysUserByAccount(sysUserRequest.getAccount());
        if (Objects.isNull(sysUserModel)) {
            throw new ApiException(RbacResultEnum.ACCOUNT_EXITS);
        }
        // 判断用户是否激活
        if (sysUserModel.getStatus() != null && sysUserModel.getStatus().equals(0)) {
            throw new ApiException(RbacResultEnum.NEED_LOGIN_BEN);
        }
        // 判断用户是否删除
        if (sysUserModel.getIsDeleted() != null && sysUserModel.getIsDeleted().equals(1)) {
            throw new ApiException(RbacResultEnum.ACCOUNT_EXITS);
        }

        // 验证码判断
        String redisCode = redisUtil.get(Constants.KAPTCHA_SESSION_KEY + ":" + sysUserRequest.getCodeuuid());
        if (StringUtils.isEmpty(redisCode)) {
            throw new ApiException(RbacResultEnum.CREATE_CODE_ERROR);
        }
        if (!redisCode.equalsIgnoreCase(sysUserRequest.getCode())) {
            throw new ApiException(RbacResultEnum.INPUT_CODE_ERROR);
        }

        //  如果存在，就开始比较密码是否相等, 如果不相同就抛出异常
        String slatPwd = MD5Util.md5slat(sysUserRequest.getPassword(), sysUserModel.getSlat());
        if (!slatPwd.equalsIgnoreCase(sysUserModel.getPassword())) {
            throw new ApiException(RbacResultEnum.INPUT_ACCOUNT_ERRIR);
        }

        // 3: 如果相等，就把model中用户信息，放入vo中进行返回
        SysUserVO sysUserVO = sysUserConvertModelToVO.convert(sysUserModel);

        Map<String, String> tokenMap = JwtUtil.generateToken(sysUserModel.getAccount(),
                sysUserModel.getUsername(),
                sysUserModel.getAvatar(),
                sysUserModel.getAdmin(),
                sysUserModel.getId()
        );

        // 创建token
        String token = tokenMap.get("token");
        sysUserVO.setToken(token);
        sysUserVO.setRefreshTime(tokenMap.get("refreshTime"));

        // 根据用户id查询用户对应角色信息
        List<SysRoleModel> sysRoleModels = sysRoleRepository.findRolesByUserId(sysUserModel.getId());
        if ( CollectionUtils.isEmpty(sysRoleModels) ) {
            throw new BusinessException(ROLE_API_EX);
        }
        sysUserVO.setRoles(sysRoleModels);
        // 根据角色列表，取出第一个角色信息
        if (!CollectionUtils.isEmpty(sysRoleModels)) {
            // 角色对应的菜单
            Long firstRoleId = sysRoleModels.get(0).getId();
            List<SysMenusModel> sysMenusList = sysMenusRepository.findSysMenusList(firstRoleId);
            sysUserVO.setMenus(sysMenusList);
            // 角色对应的apis
            List<SysApisModel> apisList = sysApisRepository.findRoleApis(firstRoleId);
            sysUserVO.setPermissions(apisList);

            // 把当前角色对应的权限放入到redis，方便后续的拦截器进行控制和使用
            redisUtil.set(SysUserContants.RBAC_PERMISSION_LIST + sysUserModel.getId(), JsonUtil.obj2String(apisList), JwtUtil.EXPIRE_TIME, TimeUnit.MILLISECONDS);
        }

        // 把 token 放入 redis 来做用户挤下线
        redisUtil.set(SysUserContants.LOGIN_REDIS_TOKEN_KEY + sysUserModel.getId(), token, JwtUtil.EXPIRE_TIME, TimeUnit.MILLISECONDS);

        // 删除缓存中的验证码
        redisUtil.del(Constants.KAPTCHA_SESSION_KEY + ":" + sysUserRequest.getCodeuuid());

        // 结果返回
        return sysUserVO;
    }

    @Override
    public SysUserDetailVO selectById(Long id) {
        SysUserModel sysUserModel = sysUserRepository.selectBySysUserById(id);
        SysUserDetailVO sysUserDetailVO = sysUserConvertModelToVO.convertDetail(sysUserModel);
        return sysUserDetailVO;
    }
}
